﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using LumiSoft.Net;
using LumiSoft.Net.SDP;
using LumiSoft.Net.SIP;
using LumiSoft.Net.SIP.Debug;
using LumiSoft.Net.SIP.Stack;
using LumiSoft.Net.SIP.Message;
using LumiSoft.Net.AUTH;
using System.Net;
using LumiSoft.SIP.UA;
using System.Threading;
using LumiSoft.Net.SIP.Proxy;
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        #region class UserInfo

        /// <summary>
        /// This class holds user info.
        /// </summary>
        private class UserInfo
        {
            private string m_UserName = "";
            private string m_Password = "";
            private string m_AOR = "";

            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="userName">User name.</param>
            /// <param name="password">Password.</param>
            /// <param name="aor">Address of record.</param>
            public UserInfo(string userName, string password, string aor)
            {
                m_UserName = userName;
                m_Password = password;
                m_AOR = aor;
            }


            #region Properties Implementation

            /// <summary>
            /// Gets user name.
            /// </summary>
            public string UserName
            {
                get { return m_UserName; }
            }

            /// <summary>
            /// Gets password.
            /// </summary>
            public string Password
            {
                get { return m_Password; }
            }

            /// <summary>
            /// Gets address of record.
            /// </summary>
            public string AddressOfRecord
            {
                get { return m_AOR; }
            }

            #endregion

        }

        #endregion
        private SIP_Stack m_pStack = null;
        private int m_SipPort = 7668;
        private bool m_IsClosing = false;
     //   private SIP_Call m_pCall = null;
        private SIP_UA_Registration reg;
        private AbsoluteUri contact = null;
        private SIP_RequestSender m_pRegisterSender = null;
        private string localIP = "44.4.2.234";
        private string toAor = string.Empty;
        private string fromAor = string.Empty;
        SIP_Uri server = null;
       // SIP_RequestSender senderInvite = null;
        private object inviteLock = new object();
        private List<UserInfo> m_pUsers = null;
        List<SIP_RequestSender> ptzSenders = new List<SIP_RequestSender>();
        List<SIP_RequestSender> regDevSenderList = new List<SIP_RequestSender>();
        private SIP_Proxy proxy = null;
        List<SIP_Call> sipCalls = new List<SIP_Call>();
        bool canRegisterDevices = false;

        private SIP_Call subscribeCall = null;
      
      
        public Form1()
        {
            InitializeComponent();
        }

        private void btnReg_Click(object sender, EventArgs e)
        {
            

            server = new SIP_Uri();
            server.User = txtUACode.Text;
            server.Host = txtServerIP.Text;
            server.Port = int.Parse(txtServerPort.Text);
           // contact = AbsoluteUri.Parse(string.Format("sip:100@{0}:{1}",localIP,m_SipPort));
            m_pStack.Credentials.Add(new NetworkCredential(txtUser.Text, txtUser.Text,txtServerIP.Text )); 
             reg=m_pStack.CreateRegistration(server,fromAor ,contact , 30);
             reg.Error += new EventHandler<SIP_ResponseReceivedEventArgs>(reg_Error);
             reg.Registered += delegate(object o, EventArgs e1) { 
                 canRegisterDevices = true;
                 btnRegDevice.BeginInvoke(new MethodInvoker(delegate(){   btnRegDevice.Enabled = true;}));
              
             };
            
            reg.BeginRegister(true);


        }

        void reg_Error(object sender, SIP_ResponseReceivedEventArgs e)
        {
            canRegisterDevices = false;
             btnRegDevice.BeginInvoke(new MethodInvoker(delegate(){   btnRegDevice.Enabled = false;}));
            //SIP_Response rep = e.Response;
            //if (rep.StatusCode == 407)
            //{
               

            //    SIP_Request cReq = e.ClientTransaction.Request.Copy();
            //    cReq.CSeq.SequenceNumber = m_pStack.ConsumeCSeq();
            //    string tmp = rep.ProxyAuthenticate.GetFirst().Value.ToLower();
            //    tmp = tmp.Substring(tmp.IndexOf("digest") + "digest".Length);
            //    Auth_HttpDigest auth = new Auth_HttpDigest("100", "100", null, server.Scheme+":"+fromAor, tmp, cReq.RequestLine.Method);
            //    cReq.ProxyAuthorization.Add(auth.ToString());
                
            //    m_pStack.TransportLayer.SendRequest(cReq);
                

            //}
             
        }

        void reg_StateChanged(object sender, EventArgs e)
        {
            
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
           
        }
        /// <summary>
        /// Is called when SIp stack has unhandled error.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pStack_Error(object sender,ExceptionEventArgs e)
        {

            if (!m_IsClosing)
            {
                listBox1.BeginInvoke(new MethodInvoker(delegate(){
                   listBox1.Items.Add("Error:" + e.Exception.Message);
                }));
              
               // MessageBox.Show("Error xx: " + e.Exception.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return;
           
           

            
        }

      
   /// <summary>
        /// Is called when call state has changed.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pCall_StateChanged(object sender,EventArgs e)
        {
            #region Active
            SIP_Call call = (SIP_Call)sender;
            if (call.State == SIP_CallState.Active)
            {
                // We need invoke here, we are running on thread pool thread.
                this.BeginInvoke(new MethodInvoker(delegate(){
                    this.label1.Text="call established\n"; 
                }));
            }

            #endregion

            #region Terminated

            else if (call.State == SIP_CallState.Terminated)
            {
                sipCalls.Remove(call);
                label1.BeginInvoke(new MethodInvoker(delegate()
                {
                    label1.Text = "call terminated";
                }));
            }

            #endregion

            #region Disposed

            else if (call.State == SIP_CallState.Disposed)
            {
                if(!m_IsClosing){
                    // We need invoke here, we are running on thread pool thread.
                    this.BeginInvoke(new MethodInvoker(delegate(){
                        label1.Text = "call disposed";
                    }));
                }
                
              //  m_pCall = null;
            }

            #endregion
        }
     private void m_pStack_RequestReceived(object sender,SIP_RequestReceivedEventArgs e)
        {
         
            try
            {
                #region REGISTER
                if (e.Request.RequestLine.Method == SIP_Methods.REGISTER)
                {
                    // we match register to Registar
                //    e.IsHandled = false;
                }
                #endregion

                #region SUBSCRIBE
                else if (e.Request.RequestLine.Method == SIP_Methods.SUBSCRIBE)
                {
                    SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request, e.Flow);
                  //  response.To.Tag=SIP_Utils.CreateTag();
                    e.ServerTransaction.SendResponse(response);
                    #region Expires!=0
                    if (e.Request.Expires > 0)
                    {
                        SIP_Dialog_Subscribe dialog = (SIP_Dialog_Subscribe)m_pStack.TransactionLayer.GetOrCreateDialog(e.ServerTransaction, response);

                        // We need invoke here, otherwise we block SIP stack RequestReceived event while incoming call UI showed.
                        this.BeginInvoke(new MethodInvoker(delegate()
                        {
                            try
                            {

                                // Create call.
                                //   m_pCall = new SIP_Call(m_pStack,dialog);
                                //    m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
                                //    m_pCall_StateChanged(m_pCall,new EventArgs());
                                label1.BeginInvoke(new MethodInvoker(delegate()
                                {
                                    label1.Text = "incoming subscribe....";
                                }));



                            }
                            catch (Exception x1)
                            {
                                MessageBox.Show("Error: " + x1.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }
                        }));

                      

                        // Start retransmitting 2xx response, while ACK receives.
                        // Create call.
                      //  dialog.RequestReceived += new EventHandler<SIP_RequestReceivedEventArgs>(subscribeDialog_RequestReceived);
                        SIP_Call m_pCall = new SIP_Call(m_pStack, dialog);
                        sipCalls.Add(m_pCall);
                        m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
                        m_pCall_StateChanged(m_pCall, new EventArgs());
                       
                        subscribeCall = m_pCall;
                        //Handle2xx(m_pCall, dialog, e.ServerTransaction);
                    }
                    #endregion
                    else
                    {

                    }
                    
                }
                #endregion

                #region NOTIFY
                else if (e.Request.RequestLine.Method == SIP_Methods.NOTIFY)
                {
                    
                    if (e.Dialog == null)
                    {
                        // device notify
                        SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request);
                        e.ServerTransaction.SendResponse(response);
                    }
                   
                   
                }
                #endregion

                #region INFO
                else if (e.Request.RequestLine.Method == SIP_Methods.INFO)
                {
                    SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request);
                    e.ServerTransaction.SendResponse(response);
                }
                #endregion

                #region OPTIONS
                else if (e.Request.RequestLine.Method == SIP_Methods.OPTIONS)
                {
                    SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request);
                    e.ServerTransaction.SendResponse(response);
                }
                #endregion

                #region CANCEL

                else if (e.Request.RequestLine.Method == SIP_Methods.CANCEL)
                {
                    /* RFC 3261 9.2.
                        If the UAS did not find a matching transaction for the CANCEL
                        according to the procedure above, it SHOULD respond to the CANCEL
                        with a 481 (Call Leg/Transaction Does Not Exist).
                  
                        Regardless of the method of the original request, as long as the
                        CANCEL matched an existing transaction, the UAS answers the CANCEL
                        request itself with a 200 (OK) response.
                    */

                    SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);
                    if (trToCancel != null)
                    {
                        trToCancel.Cancel();
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request));
                    }
                    else
                    {
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request));
                    }
                }

                #endregion

                #region BYE

                else if (e.Request.RequestLine.Method == SIP_Methods.BYE)
                {
                    /* RFC 3261 15.1.2.
                        If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481
                        (Call/Transaction Does Not Exist) response and pass that to the server transaction.
                    */

                    // Currently we match BYE to dialog and it processes it,
                    // so BYE what reaches here doesnt match to any dialog.

                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request));
                }

                #endregion

                #region INVITE

                else if (e.Request.RequestLine.Method == SIP_Methods.INVITE)
                {

                    #region Incoming call
                    e.IsHandled = true;
                    if (e.Dialog == null)
                    {


                        // Send ringing to remote-party.
                        SIP_Response responseRinging = m_pStack.CreateResponse(SIP_ResponseCodes.x180_Ringing, e.Request, e.Flow);
                        responseRinging.To.Tag = SIP_Utils.CreateTag();
                        e.ServerTransaction.SendResponse(responseRinging);

                        SIP_Dialog_Invite dialog = (SIP_Dialog_Invite)m_pStack.TransactionLayer.GetOrCreateDialog(e.ServerTransaction, responseRinging);

                        // We need invoke here, otherwise we block SIP stack RequestReceived event while incoming call UI showed.
                        this.BeginInvoke(new MethodInvoker(delegate()
                        {
                            try
                            {

                                // Create call.
                                //   m_pCall = new SIP_Call(m_pStack,dialog);
                                //    m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
                                //    m_pCall_StateChanged(m_pCall,new EventArgs());
                                label1.BeginInvoke(new MethodInvoker(delegate()
                                {
                                    label1.Text = "incoming call....";
                                }));



                            }
                            catch (Exception x1)
                            {
                                MessageBox.Show("Error: " + x1.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }
                        }));

                        dialog.RequestReceived += new EventHandler<SIP_RequestReceivedEventArgs>(inviteDialog_RequestReceived);
                        SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.ServerTransaction.Request, e.ServerTransaction.Flow);
                        e.ServerTransaction.SendResponse(response);

                        // Start retransmitting 2xx response, while ACK receives.
                        // Create call.
                        SIP_Call m_pCall = new SIP_Call(m_pStack, dialog);
                        sipCalls.Add(m_pCall);
                        m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
                        m_pCall_StateChanged(m_pCall, new EventArgs());
                        Handle2xx(m_pCall, dialog, e.ServerTransaction);

                    }

                    #endregion

                    #region Re-INVITE

                    else
                    {

                    }

                    #endregion
                }

                #endregion

                #region ACK

                else if (e.Request.RequestLine.Method == SIP_Methods.ACK)
                {
                   
                }

                #endregion

                #region Other

                else
                {
                    // ACK is response less method.
                    if (e.Request.RequestLine.Method != SIP_Methods.ACK)
                    {
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented, e.Request));
                    }
                }

                #endregion
            }
            catch{
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error,e.Request));
            }
        }

     private SIP_Call FindCall(SIP_Dialog dialog)
     {
        foreach (SIP_Call call in sipCalls)
        {
            if (call.Dialog == dialog)
            {
                return call;
            }
        }
        return null;
         
     }
     #region
     /// <summary>
        /// This method takes care of INVITE 2xx response retransmissions while ACK received.
        /// </summary>
        /// <param name="dialog">SIP dialog.</param>
        /// <param name="transaction">INVITE server transaction.</param>
        /// <exception cref="ArgumentException">Is raised when <b>dialog</b>,<b>transaction</b> is null reference.</exception>
        private void Handle2xx(SIP_Call pCall,SIP_Dialog dialog,SIP_ServerTransaction transaction)
        {
            if(dialog == null){
                throw new ArgumentNullException("dialog");
            }
            if(transaction == null){
                throw new ArgumentException("transaction");
            }
            #region RFC 6026 8.1.
            /* RFC 6026 8.1.
                Once the response has been constructed, it is passed to the INVITE
                server transaction.  In order to ensure reliable end-to-end
                transport of the response, it is necessary to periodically pass
                the response directly to the transport until the ACK arrives.  The
                2xx response is passed to the transport with an interval that
                starts at T1 seconds and doubles for each retransmission until it
                reaches T2 seconds (T1 and T2 are defined in Section 17).
                Response retransmissions cease when an ACK request for the
                response is received.  This is independent of whatever transport
                protocols are used to send the response.
             
                If the server retransmits the 2xx response for 64*T1 seconds without
                receiving an ACK, the dialog is confirmed, but the session SHOULD be
                terminated.  This is accomplished with a BYE, as described in Section
                15.
              
                 T1 - 500
                 T2 - 4000
            */
            #endregion
            TimerEx timer = null;
            
            EventHandler<SIP_RequestReceivedEventArgs> callback = delegate(object s1,SIP_RequestReceivedEventArgs e){
                try{
                    if(e.Request.RequestLine.Method == SIP_Methods.ACK){
                        // ACK for INVITE 2xx response received, stop retransmitting INVITE 2xx response.
                        if(transaction.Request.CSeq.SequenceNumber == e.Request.CSeq.SequenceNumber){
                            if(timer != null){
                                timer.Dispose();
                                timer = null;
                                pCall.StartReceiveAck(e.Request.Expires);
                            }
                        }
                    }
                }
                catch{
                    // We don't care about errors here.
                }
            };
            dialog.RequestReceived += callback;
                
            // Create timer and sart retransmitting INVITE 2xx response.
            timer = new TimerEx(500);
            timer.AutoReset = false;
            timer.Elapsed += delegate(object s,System.Timers.ElapsedEventArgs e){
                try{
                    lock(transaction.SyncRoot){
                        if (transaction.State == SIP_TransactionState.Accpeted)
                        {
                            transaction.SendResponse(transaction.FinalResponse);
                        }
                        else{
                            timer.Dispose();
                            timer = null;
                            pCall.StartReceiveAck(transaction.Request.Expires);
                            return;
                        }
                    }

                    timer.Interval = Math.Min(timer.Interval * 2,4000);
                    timer.Enabled = true;
                }
                catch{
                    // We don't care about errors here.
                }
            };
            timer.Disposed += delegate(object s1,EventArgs e1){
                try{
                    dialog.RequestReceived -= callback;
                }
                catch{
                    // We don't care about errors here.
                }
            };
            timer.Enabled = true;                       
        }

        #endregion
     private void btnInvite_Click(object sender, EventArgs ex)
     {
         
         //toAor = string.Format("{0}@{1}:{2}", txtTo.Text, localIP,7666);
         // toAor 101@44.4.2.235:7666
         toAor = txtTo.Text;
       
         // from sip:100@44.4.2.234  to sip:101@44.4.2.235:7666
         SIP_Request inviteReq = m_pStack.CreateRequest(SIP_Methods.INVITE, new SIP_t_NameAddress("sip:" + toAor), new SIP_t_NameAddress("sip:" + fromAor));
         // sip:101@44.4.2.235:7666
         inviteReq.RequestLine.Uri = SIP_Uri.Parse("sip:" + toAor);
         inviteReq.Contact.Add("<" + contact + ">");
         inviteReq.ContentType = "application/IVS_XML";
       //  inviteReq.ContentType = "application/sdp";
         

         
        SIP_RequestSender  senderInvite = m_pStack.CreateRequestSender(inviteReq);
          // Create call.
         SIP_Call m_pCall = new SIP_Call(m_pStack, senderInvite);
         sipCalls.Add(m_pCall);
          m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
         bool finalResponseSeen = false;
         List<SIP_Dialog_Invite> earlyDialogs = new List<SIP_Dialog_Invite>();
         senderInvite.ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs e)
         {
             // Skip 2xx retransmited response.
             if (finalResponseSeen)
             {
                 return;
             }
             if (e.Response.StatusCode >= 200)
             {
                 finalResponseSeen = true;
             }

             try
             {
                 #region Provisional

                 if (e.Response.StatusCodeType == SIP_StatusCodeType.Provisional)
                 {
                     /* RFC 3261 13.2.2.1.
                         Zero, one or multiple provisional responses may arrive before one or
                         more final responses are received.  Provisional responses for an
                         INVITE request can create "early dialogs".  If a provisional response
                         has a tag in the To field, and if the dialog ID of the response does
                         not match an existing dialog, one is constructed using the procedures
                         defined in Section 12.1.2.
                     */
                     if (e.Response.StatusCode > 100 && e.Response.To.Tag != null)
                     {
                         earlyDialogs.Add((SIP_Dialog_Invite)e.GetOrCreateDialog);
                     }

                     // 180_Ringing.
                     if (e.Response.StatusCode == 180)
                     {
                       

                         // We need BeginInvoke here, otherwise we block client transaction.
                         label1.BeginInvoke(new MethodInvoker(delegate()
                         {
                            label1.Text = "Ringing";
                         }));
                     }
                 }

                 #endregion

                 #region Success

                 else if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
                 {
                    // SIP_Dialog dialog = e.GetOrCreateDialog;
                      SIP_Dialog dialog = e.GetOrCreateDialog;
                     
                     /* Exit all all other dialogs created by this call (due to forking).
                        That is not defined in RFC but, since UAC can send BYE to early and confirmed dialogs, 
                        all this is 100% valid.
                     */
                     foreach (SIP_Dialog_Invite d in earlyDialogs.ToArray())
                     {
                         if (!d.Equals(dialog))
                         {
                             d.Terminate("Another forking leg accepted.", true);
                         }
                     }
                    
                     SIP_Request ackRequest=HandleAck(dialog, e.ClientTransaction);
                    

                     // Remote-party provided SDP.
                    
                        try
                        {
                             m_pCall.InitCalling(dialog, ackRequest);
                          

                        }
                        catch
                        {
                            label1.BeginInvoke(new MethodInvoker(delegate()
                            {
                                label1.Text = "SDP answer parsing/processing failed.";
                            }));   
                            
                        }
                    
                    

                 }

                 #endregion

                 #region Failure

                 else
                 {
                     /* RFC 3261 13.2.2.3.
                         All early dialogs are considered terminated upon reception of the non-2xx final response.
                     */
                     foreach (SIP_Dialog_Invite dialog in earlyDialogs.ToArray())
                     {
                      //   if (dialog == m_pCall.Dialog) ;
                         dialog.Terminate("All early dialogs are considered terminated upon reception of the non-2xx final response. (RFC 3261 13.2.2.3)", false);
                     }

                     // We need BeginInvoke here, otherwise we block client transaction while message box open.
                         this.BeginInvoke(new MethodInvoker(delegate()
                         {
                             MessageBox.Show("Calling failed: " + e.Response.StatusCode_ReasonPhrase, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                         }));

                     // We need BeginInvoke here, otherwise we block client transaction.
                     label1.BeginInvoke(new MethodInvoker(delegate()
                     {
                         label1.Text = "";
                     }));
                     m_pCall.Terminate("failed to call");

                 }

                 #endregion
             }
             catch (Exception x)
             {
                 // We need BeginInvoke here, otherwise we block client transaction while message box open.
                 this.BeginInvoke(new MethodInvoker(delegate()
                 {
                     MessageBox.Show("Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                 }));
             }
         };

         label1.Text = "Calling";

         // Start calling.
         
         senderInvite.Start();

     }
     #region method HandleAck

     /// <summary>
     /// This method takes care of ACK sending and 2xx response retransmission ACK sending.
     /// </summary>
     /// <param name="dialog">SIP dialog.</param>
     /// <param name="transaction">SIP client transaction.</param>
     private SIP_Request HandleAck(SIP_Dialog dialog, SIP_ClientTransaction transaction)
     {
         if (dialog == null)
         {
             throw new ArgumentNullException("dialog");
         }
         if (transaction == null)
         {
             throw new ArgumentNullException("transaction");
         }

         /* RFC 3261 6.
             The ACK for a 2xx response to an INVITE request is a separate transaction.
              
            RFC 3261 13.2.2.4.
             The UAC core MUST generate an ACK request for each 2xx received from
             the transaction layer.  The header fields of the ACK are constructed
             in the same way as for any request sent within a dialog (see Section
             12) with the exception of the CSeq and the header fields related to
             authentication.  The sequence number of the CSeq header field MUST be
             the same as the INVITE being acknowledged, but the CSeq method MUST
             be ACK.  The ACK MUST contain the same credentials as the INVITE.  If
             the 2xx contains an offer (based on the rules above), the ACK MUST
             carry an answer in its body.
         */

         SIP_t_ViaParm via = new SIP_t_ViaParm();
         via.ProtocolName = "SIP";
         via.ProtocolVersion = "2.0";
         via.ProtocolTransport = transaction.Flow.Transport;
         via.SentBy = new HostEndPoint(transaction.Flow.LocalEP);
         via.Branch = SIP_t_ViaParm.CreateBranch();
         via.RPort = 0;

         SIP_Request ackRequest = dialog.CreateRequest(SIP_Methods.ACK);
         ackRequest.Expires = 5;
         ackRequest.Via.AddToTop(via.ToStringValue());
         ackRequest.CSeq = new SIP_t_CSeq(transaction.Request.CSeq.SequenceNumber, SIP_Methods.ACK);
         // Authorization
         foreach (SIP_HeaderField h in transaction.Request.Authorization.HeaderFields)
         {
             ackRequest.Authorization.Add(h.Value);
         }
         // Proxy-Authorization 
         foreach (SIP_HeaderField h in transaction.Request.ProxyAuthorization.HeaderFields)
         {
             ackRequest.Authorization.Add(h.Value);
         }

         // Send ACK.
         SendAck(dialog, ackRequest);

         // Start receive 2xx retransmissions.
         transaction.ResponseReceived += delegate(object sender, SIP_ResponseReceivedEventArgs e)
         {
             if (dialog.State == SIP_DialogState.Disposed || dialog.State == SIP_DialogState.Terminated)
             {
                 return;
             }

             // Don't send ACK for forked 2xx, our sent BYE(to all early dialogs) or their early timer will kill these dialogs.
             // Send ACK only to our accepted dialog 2xx response retransmission.
             if (e.Response.From.Tag == ackRequest.From.Tag && e.Response.To.Tag == ackRequest.To.Tag)
             {
                 SendAck(dialog, ackRequest);
             }
         };

         return ackRequest; 
        
     }

     #endregion

     #region method SendAck

     /// <summary>
     /// Sends ACK to remote-party.
     /// </summary>
     /// <param name="dialog">SIP dialog.</param>
     /// <param name="ack">SIP ACK request.</param>
     private void SendAck(SIP_Dialog dialog, SIP_Request ack)
     {
         if (dialog == null)
         {
             throw new ArgumentNullException("dialog");
         }
         if (ack == null)
         {
             throw new ArgumentNullException("ack");
         }
         if (dialog.State == SIP_DialogState.Disposed || dialog.State == SIP_DialogState.Terminated)
         {

             return;
         }
         try
         {
             // Try existing flow.
             dialog.Flow.Send(ack);

             // Log
             if (dialog.Stack.Logger != null)
             {
                 byte[] ackBytes = ack.ToByteData();

                 dialog.Stack.Logger.AddWrite(
                     dialog.ID,
                     null,
                     ackBytes.Length,
                     "Request [DialogID='" + dialog.ID + "';" + "method='" + ack.RequestLine.Method + "'; cseq='" + ack.CSeq.SequenceNumber + "'; " +
                     "transport='" + dialog.Flow.Transport + "'; size='" + ackBytes.Length + "'] sent '" + dialog.Flow.LocalEP + "' -> '" + dialog.Flow.RemoteEP + "'.",
                     dialog.Flow.LocalEP,
                     dialog.Flow.RemoteEP,
                     ackBytes
                 );
             }
         }
         catch
         {
             /* RFC 3261 13.2.2.4.
                 Once the ACK has been constructed, the procedures of [4] are used to
                 determine the destination address, port and transport.  However, the
                 request is passed to the transport layer directly for transmission,
                 rather than a client transaction.
             */
             try
             {
                 dialog.Stack.TransportLayer.SendRequest(ack);
             }
             catch (Exception x)
             {
                 // Log
                 if (dialog.Stack.Logger != null)
                 {
                     dialog.Stack.Logger.AddText("Dialog [id='" + dialog.ID + "'] ACK send for 2xx response failed: " + x.Message + ".");
                 }
             }
         }
     }

     #endregion
     void sender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
     {
         
     }

     private void btnStart_Click(object sender, EventArgs e)
     {
         btnRegDevice.Enabled = false;
         localIP = txtLocalIP.Text;
         m_SipPort = int.Parse(txtPort.Text);

         fromAor = string.Format("{0}@{1}", txtUser.Text, localIP);
         // fromAor  100@44.4.2.234
         contact = AbsoluteUri.Parse(string.Format("sip:{0}@{1}:{2}",txtUser.Text, localIP,m_SipPort));
         //contact sip:100@44.4.2.234
         m_pStack = new SIP_Stack();
         m_pStack.UserAgent = "LumiSoft SIP UA 1.0";

         //   ip 44.4.2.234 port 7668
         m_pStack.BindInfo = new IPBindInfo[] { new IPBindInfo("", BindInfoProtocol.UDP, IPAddress.Parse(localIP), m_SipPort) };
     
         m_pStack.Error += new EventHandler<ExceptionEventArgs>(m_pStack_Error);
         m_pStack.RequestReceived += new EventHandler<SIP_RequestReceivedEventArgs>(m_pStack_RequestReceived);

         if (chkServer.Checked)
         {
             proxy = new SIP_Proxy(m_pStack);
             proxy.ProxyMode = SIP_ProxyMode.Registrar|SIP_ProxyMode.Stateless;
             proxy.AddressExists += new SIP_AddressExistsEventHandler(proxy_AddressExists);
             proxy.Authenticate += new SIP_AuthenticateEventHandler(proxy_Authenticate);
             proxy.Registrar.CanRegister += new SIP_CanRegisterEventHandler(Registrar_CanRegister);
             proxy.IsLocalUri += new SIP_IsLocalUriEventHandler(proxy_IsLocalUri);
             proxy.Registrar.AorRegistered += new EventHandler<SIP_RegistrationEventArgs>(Registrar_AorRegistered);
             m_pUsers = new List<UserInfo>();
             UserInfo userInfo = new UserInfo("100", "100", "100@44.4.2.234");
             m_pUsers.Add(userInfo);
             userInfo = new UserInfo("101", "101", "101@44.4.2.235");
             m_pUsers.Add(userInfo);
            
         }
         /*
         ((SIP_ProxyCore)m_pSipStack.SipCore).AddressExists += new SIP_AddressExistsEventHandler(SIP_AddressExists);
         ((SIP_ProxyCore)m_pSipStack.SipCore).Authenticate += new SIP_AuthenticateEventHandler(SIP_Authenticate);
         ((SIP_ProxyCore)m_pSipStack.SipCore).Registrar.CanRegister += new SIP_CanRegisterEventHandler(SIP_CanRegister);
         ((SIP_ProxyCore)m_pSipStack.SipCore).IsLocalUri += new SIP_IsLocalUriEventHandler(SIP_IsLocalUri);
         ((SIP_ProxyCore)m_pSipStack.SipCore).GetGateways += new SIP_GetGatewaysEventHandler(SIP_GetGateways);
          * */
         m_pStack.Realm = txtLocalIP.Text;
         string s=string.Join(",", m_pStack.Allow.ToArray());
         m_pStack.MinimumExpireTime = 30;
         m_pStack.Allow.Add(SIP_Methods.SUBSCRIBE);
         m_pStack.Allow.Add(SIP_Methods.NOTIFY);
         m_pStack.Start();


         wfrm_SIP_Debug debug = new wfrm_SIP_Debug(m_pStack);
         debug.Show();
     }

     void Registrar_AorRegistered(object sender, SIP_RegistrationEventArgs e)
     {
     }

     bool proxy_IsLocalUri(string uri)
     {
         lock (m_pUsers)
         {
             foreach (UserInfo userInfo in m_pUsers)
             {
                 if (userInfo.AddressOfRecord.IndexOf('@') > -1)
                 {
                     string d = userInfo.AddressOfRecord.Split('@')[1];
                     if (d.ToLower() == uri.ToLower())
                     {
                         return true;
                     }
                 }
             }
         }

         return false;
     }

     bool Registrar_CanRegister(string userName, string address)
     {
         lock (m_pUsers)
         {
             // Search for specified user.
             foreach (UserInfo userInfo in m_pUsers)
             {
                 // Wanted user, do uth.
                 if (userInfo.UserName == userName)
                 {
                     if (userInfo.AddressOfRecord.ToLower() == address.ToLower())
                     {
                         return true;
                     }
                     else
                     {
                         return false;
                     }
                 }
             }
         }

         return false;
     }

     void proxy_Authenticate(SIP_AuthenticateEventArgs e)
     {
         lock (m_pUsers)
         {
             // Search for specified user.
             foreach (UserInfo userInfo in m_pUsers)
             {
                 // Wanted user, do uth.
                 if (userInfo.UserName == e.AuthContext.UserName)
                 {
                        e.Authenticated = e.AuthContext.Authenticate(userInfo.UserName,userInfo.Password);
                     //  e.Authenticated = true;
                         return;
                 }
             }
         }
     }

     bool proxy_AddressExists(string address)
     {
         lock (m_pUsers)
         {
             // Search for specified address.
             foreach (UserInfo userInfo in m_pUsers)
             {
                 // Wanted user, do uth.
                 //TODO: 需要修订成只匹配IP
                 if (userInfo.AddressOfRecord.ToLower() == address.ToLower())
                 {
                     return true;
                 }
             }
         }

         return false;
     }

     private void Form1_FormClosing(object sender, FormClosingEventArgs e)
     {
         if(m_pStack!=null)
         m_pStack.Dispose();
     }

     private void btnBye_Click(object sender, EventArgs e)
     {
         foreach (SIP_Call call in sipCalls)
         {
             if (call != null && call.State == SIP_CallState.Active)
                 call.Terminate("finished");
         }
         
         
     }

     private void btnPTZ_Click(object sender, EventArgs e)
     {
         toAor = txtTo.Text;
         SIP_Request ptzReq = m_pStack.CreateRequest(SIP_Methods.INFO, new SIP_t_NameAddress("sip:" +toAor), new SIP_t_NameAddress("sip:" + fromAor));
         ptzReq.RequestLine.Uri = SIP_Uri.Parse("sip:" + toAor);
         ptzReq.Contact.Add("<" + contact + ">");
         ptzReq.ContentType = "application/sdp";
         SIP_RequestSender ptzReqSender = m_pStack.CreateRequestSender(ptzReq);
         ptzSenders.Add(ptzReqSender);
         ptzReqSender.ResponseReceived += delegate(object reqSender, SIP_ResponseReceivedEventArgs e1)
         {
             ptzSenders.Remove(ptzReqSender);
               try
             {
                 #region Provisional

                 
                 #endregion

                 #region Success
                   
                 if (e1.Response.StatusCodeType == SIP_StatusCodeType.Success)
                 {

                    
                     listBox1.BeginInvoke(new MethodInvoker(delegate()
                     {
                         listBox1.Items.Add("ptz success");
                     }));

                 }

                 #endregion

                 #region Failure

                 else
                 {

                     listBox1.BeginInvoke(new MethodInvoker(delegate()
                     {
                         listBox1.Items.Add("ptz failed");
                     }));
                   

                 }

                 #endregion
             }
             catch (Exception x)
             {
                 // We need BeginInvoke here, otherwise we block client transaction while message box open.
                 this.BeginInvoke(new MethodInvoker(delegate()
                 {
                     MessageBox.Show("Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                 }));
             }
         };
         listBox1.Items.Add("ptz start");
         ptzReqSender.Start();
         
         
     }

     private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
     {

     }

     private void btnRefresh_Click(object sender, EventArgs e)
     {
         if (proxy != null)
         {
             listBox2.Items.Clear();
             foreach (SIP_Registration r in proxy.Registrar.Registrations)
            {
                listBox2.Items.Add(r.UserName + "," + r.AOR + "," + r.CreateTime.ToString("yyyy-MM-dd hh:mm:ss"));
               
            }
            
         }
     }

     private void btnRegDevice_Click(object sender, EventArgs e)
     {
         toAor=txtTo.Text;
         SIP_Request notifyDevReq = m_pStack.CreateRequest(SIP_Methods.NOTIFY, new SIP_t_NameAddress("sip:" + toAor), new SIP_t_NameAddress("sip:" + fromAor));
         notifyDevReq.RequestLine.Uri = SIP_Uri.Parse("sip:" + toAor);
         notifyDevReq.Contact.Add("<" + contact + ">");
         notifyDevReq.ContentType = "application/sdp";
         SIP_RequestSender regDevSender = m_pStack.CreateRequestSender(notifyDevReq);
         regDevSenderList.Add(regDevSender);
         regDevSender.ResponseReceived += delegate(object reqSender, SIP_ResponseReceivedEventArgs e1)
         {
             regDevSenderList.Remove(regDevSender);
             try
             {
                 #region Provisional


                 #endregion

                 #region Success

                 if (e1.Response.StatusCodeType == SIP_StatusCodeType.Success)
                 {


                     listBox1.BeginInvoke(new MethodInvoker(delegate()
                     {
                         listBox1.Items.Add("register device success");
                     }));

                 }

                 #endregion

                 #region Failure

                 else
                 {

                     listBox1.BeginInvoke(new MethodInvoker(delegate()
                     {
                         listBox1.Items.Add("register device failed");
                     }));


                 }

                 #endregion
             }
             catch (Exception x)
             {
                 // We need BeginInvoke here, otherwise we block client transaction while message box open.
                 this.BeginInvoke(new MethodInvoker(delegate()
                 {
                     MessageBox.Show("Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                 }));
             }
         };
         listBox1.Items.Add("register device start");
         regDevSender.Start();
     }

    

     private void btnSubscribe_Click(object sender, EventArgs e1)
     {
        
         //toAor = string.Format("{0}@{1}:{2}", txtTo.Text, localIP,7666);
         // toAor 101@44.4.2.235:7666
         toAor = txtTo.Text;

         // from sip:100@44.4.2.234  to sip:101@44.4.2.235:7666
         SIP_Request subscribeReq = m_pStack.CreateRequest(SIP_Methods.SUBSCRIBE, new SIP_t_NameAddress("sip:" + toAor), new SIP_t_NameAddress("sip:" + fromAor));
         // sip:101@44.4.2.235:7666
         subscribeReq.RequestLine.Uri = SIP_Uri.Parse("sip:" + toAor);
         subscribeReq.Contact.Add("<" + contact + ">");
         subscribeReq.ContentType = "application/IVS_XML";
         subscribeReq.Expires = 30;
        // subscribeReq.ContentType = "application/sdp";



         SIP_RequestSender senderSubscribe = m_pStack.CreateRequestSender(subscribeReq);
         // Create call.
         SIP_Call m_pCall = new SIP_Call(m_pStack, senderSubscribe);
         sipCalls.Add(m_pCall);
         m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
         bool finalResponseSeen = false;
         List<SIP_Dialog_Subscribe> earlyDialogs = new List<SIP_Dialog_Subscribe>();
         senderSubscribe.ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs e)
         {
             // Skip 2xx retransmited response.
             if (finalResponseSeen)
             {
                 return;
             }
             if (e.Response.StatusCode >= 200)
             {
                 finalResponseSeen = true;
             }

             try
             {
                 #region Provisional

                 if (e.Response.StatusCodeType == SIP_StatusCodeType.Provisional)
                 {
                     /* RFC 3261 13.2.2.1.
                         Zero, one or multiple provisional responses may arrive before one or
                         more final responses are received.  Provisional responses for an
                         INVITE request can create "early dialogs".  If a provisional response
                         has a tag in the To field, and if the dialog ID of the response does
                         not match an existing dialog, one is constructed using the procedures
                         defined in Section 12.1.2.
                     */
                     if (e.Response.StatusCode > 100 && e.Response.To.Tag != null)
                     {
                         earlyDialogs.Add((SIP_Dialog_Subscribe)e.GetOrCreateDialog);
                     }

                     // 180_Ringing.
                     if (e.Response.StatusCode == 180)
                     {


                         // We need BeginInvoke here, otherwise we block client transaction.
                         label1.BeginInvoke(new MethodInvoker(delegate()
                         {
                             label1.Text = "Ringing";
                         }));
                     }
                 }

                 #endregion

                 #region Success

                 else if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
                 {
                     // SIP_Dialog dialog = e.GetOrCreateDialog;
                     SIP_Dialog dialog = e.GetOrCreateDialog;
                     dialog.RequestReceived += new EventHandler<SIP_RequestReceivedEventArgs>(subscribeDialog_RequestReceived);
                     /* Exit all all other dialogs created by this call (due to forking).
                        That is not defined in RFC but, since UAC can send BYE to early and confirmed dialogs, 
                        all this is 100% valid.
                     */
                     foreach (SIP_Dialog_Subscribe d in earlyDialogs.ToArray())
                     {
                         if (!d.Equals(dialog))
                         {
                             d.Terminate("Another forking leg accepted.", true);
                         }
                     }

                    // SIP_Request ackRequest = HandleAck(dialog, e.ClientTransaction);


                     // Remote-party provided SDP.

                     try
                     {
                         listBox1.BeginInvoke(new MethodInvoker(delegate()
                         {
                             listBox1.Items.Add("subscribe ok");
                         }));
                         m_pCall.InitCalling(dialog, null);


                     }
                     catch
                     {
                         label1.BeginInvoke(new MethodInvoker(delegate()
                         {
                             label1.Text = "SDP answer parsing/processing failed.";
                         }));

                     }



                 }

                 #endregion

                 #region Failure

                 else
                 {
                     /* RFC 3261 13.2.2.3.
                         All early dialogs are considered terminated upon reception of the non-2xx final response.
                     */
                     foreach (SIP_Dialog_Subscribe dialog in earlyDialogs.ToArray())
                     {
                         //   if (dialog == m_pCall.Dialog) ;
                         dialog.Terminate("All early dialogs are considered terminated upon reception of the non-2xx final response. (RFC 3261 13.2.2.3)", false);
                     }

                     // We need BeginInvoke here, otherwise we block client transaction while message box open.
                     this.BeginInvoke(new MethodInvoker(delegate()
                     {
                         MessageBox.Show("Calling failed: " + e.Response.StatusCode_ReasonPhrase, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                     }));

                     // We need BeginInvoke here, otherwise we block client transaction.
                     label1.BeginInvoke(new MethodInvoker(delegate()
                     {
                         label1.Text = "";
                     }));
                     m_pCall.Terminate("failed to call");

                 }

                 #endregion
             }
             catch (Exception x)
             {
                 // We need BeginInvoke here, otherwise we block client transaction while message box open.
                 this.BeginInvoke(new MethodInvoker(delegate()
                 {
                     MessageBox.Show("Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                 }));
             }
         };

        // label1.Text = "Calling";
         listBox1.Items.Add("start subscribe");
         // Start calling.

         senderSubscribe.Start();
     }

     private void btnNotify_Click(object sender, EventArgs e)
     {
         if (subscribeCall != null && subscribeCall.State == SIP_CallState.Active)
         {
             SIP_Dialog dialog = subscribeCall.Dialog;
             if (dialog != null)
             {
                 SIP_Request notifyReq = dialog.CreateRequest(SIP_Methods.NOTIFY);
                 ((SIP_Dialog_Subscribe)dialog).Notify(notifyReq);
             }
         }
     }

     private void inviteDialog_RequestReceived(object sender, SIP_RequestReceivedEventArgs e)
     {
         // Abandoned ACK, just skip it.
         if (e.Dialog == null) return;
         if (e.Request.RequestLine.Method == SIP_Methods.ACK)
         {
            
             SIP_Call call = FindCall(e.Dialog);
             if (call != null)
             {
              //   e.IsHandled = true;
                 listBox1.BeginInvoke(new MethodInvoker(delegate(){
                     listBox1.Items.Add("proccess ack!");
                 }));
                 call.ReStartReceiveAck();

             }
         }
        
     }
    private void subscribeDialog_RequestReceived(object sender, SIP_RequestReceivedEventArgs e)
     {
         
         if (e.Request.RequestLine.Method == SIP_Methods.NOTIFY)
         {

                 listBox1.BeginInvoke(new MethodInvoker(delegate()
                 {
                     listBox1.Items.Add("notify received");
                 }));
                 SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request);
                 e.ServerTransaction.SendResponse(response);
        }
     }

   
    }
}
